/*
 * minet.c
 *
 *  Created on: 2019年4月27日
 *      Author: 0yfj0
 */

#include <string.h>
#include "minet.h"

/*----------------------------------------------------------------------------*/
#define minet_parse_flen(ptr) (((uint8_t*)ptr)[0])
#define minet_parse_seq(ptr) (((uint8_t*)ptr)[1])
#define minet_parse_ctrl(ptr) (((uint8_t*)ptr)[2])
#define minet_parse_ctrl_rlhops(ptr) (minet_parse_ctrl(ptr)&7)
#define minet_parse_ctrl_ftyp(ptr) ((minet_parse_ctrl(ptr)>>3)&7)
#define minet_parse_ctrl_relay(ptr) ((minet_parse_ctrl(ptr)>>6)&1)
#define minet_parse_paddr(ptr) (&((uint8_t*)ptr)[3])
#define minet_parse_payload(ptr,rlhops) (&((uint8_t*)ptr)[MINET_FLEN_MIN-2+2*(rlhops)])
#define minet_parse_dlen(flen,rlhops) ((flen)-(MINET_FLEN_MIN+2*(rlhops)))

int minet_msg_legal(minet_if_t *nif, uint8_t *buf, int len);
minet_msg_t *minet_parse(minet_if_t *nif, uint8_t *buf, int len);
int minet_ack(minet_if_t *nif, uint8_t seq, uint16_t dest);
int minet_msgfifo_push(minet_msg_t **fifo, int sz, minet_msg_t *msg);
minet_msg_t *minet_msgfifo_pop(minet_msg_t **fifo, int sz);
void minet_manage(minet_if_t *nif);
#if (MINET_GATEWAY)
int minet_node_insert(minet_if_t *nif, minet_node_t *node);
int minet_node_relay(minet_if_t *nif, uint16_t dest);
#endif

/*----------------------------------------------------------------------------*/
int minet_init(minet_if_t *nif, int nbus, int netid, uint16_t addr, uint8_t chl)
{
	if (nif == NULL)
		return -1;

	memset(nif, 0, sizeof(minet_if_t));
#if (MINET_GATEWAY)
	if (addr == MINET_ADDR_GW)
	{
		nif->tpnet = MINET_T1S_ANN;
	}
#endif
	nif->nbus = nbus;
	nif->nid = netid;
	nif->addr = addr;
	nif->chl = chl;
	minet_hal_init(nif);
	minet_hal_chl(nif, chl);
	return 0;
}

int minet_relay(minet_if_t *nif, int relay)
{
	if (nif == NULL)
		return -1;

#if (MINET_RELAY_SZ>0)
	nif->relay = relay ? 1 : 0;
#endif
	return 0;
}

int minet_ping(minet_if_t *nif, uint16_t dest, int tout)
{
	uint8_t relay;
	minet_msg_t *ping;

	if (nif == NULL)
		return -1;

	if (dest == nif->addr)
		return 0;

	relay = 1 << 6;
#if (MINET_GATEWAY)
	if (nif->addr == MINET_ADDR_GW)
	{
		if (minet_node_relay(nif, dest) == 0)
			relay = 0;
	}
#endif
#if (MINET_NODE)
	if (nif->addr != MINET_ADDR_GW)
	{
		if (dest == MINET_ADDR_GW && nif->direct == 1)
			relay = 0;
	}
#endif

	if (minet_hal_cd(nif) == 0)
	{
		ping = minet_msg_malloc(MINET_FLEN_MIN);
		if (ping == NULL)
			return -1;

		ping->buf[0] = MINET_FLEN_MIN + MINET_PINGDATA_LEN;
		ping->buf[1] = ++nif->seq;
		ping->buf[2] = relay | (MINET_FTYP_PING << 3);
		ping->buf[3] = (uint8_t) (dest >> 8);
		ping->buf[4] = (uint8_t) (dest >> 0);
		ping->buf[5] = (uint8_t) (nif->addr >> 8);
		ping->buf[6] = (uint8_t) (nif->addr >> 0);
#if (MINET_RELAY_SZ>0)
		ping->buf[7] = nif->relay;
#else
		ping->buf[7] = 0;
#endif
		ping->buf[8] = nif->err.frame;
		ping->buf[9] = nif->err.fcrc;
		ping->buf[10] = nif->err.fsign;
		ping->buf[11] = minet_sign(nif->nid, ping->buf, 11);
		ping->buf[12] = minet_crc8(ping->buf, 12);

		if (minet_hal_write(nif, ping->buf, ping->buf[0]) == ping->buf[0])
		{
			minet_msg_free(ping);
			if (minet_ack_wait(nif, tout) == 0)
			{
				nif->err.pnoack = 0;
				return 0;
			}
		}
		else
			minet_msg_free(ping);

		nif->tnet = minet_hal_tick1s() + 1;
#if (MINET_NODE)
		if (nif->err.pnoack < MINET_PNOACK_REGCHL)
			++nif->err.pnoack;
#endif
	}
	return -1;
}

#if (MINET_GATEWAY)
int minet_announce(minet_if_t *nif)
{
	minet_msg_t *ann;

	if (nif == NULL)
		return -1;

	if (minet_hal_cd(nif) == 0)
	{
		ann = minet_msg_malloc(MINET_FLEN_MIN);
		if (ann == NULL)
			return -1;

		ann->buf[0] = MINET_FLEN_MIN;
		ann->buf[1] = ++nif->seq;
		ann->buf[2] = (1 << 6) | (MINET_FTYP_ANN << 3);
		ann->buf[3] = (uint8_t) (MINET_ADDR_ALL >> 8);
		ann->buf[4] = (uint8_t) (MINET_ADDR_ALL >> 0);
		ann->buf[5] = (uint8_t) (nif->addr >> 8);
		ann->buf[6] = (uint8_t) (nif->addr >> 0);
		ann->buf[7] = minet_sign(nif->nid, ann->buf, 7);
		ann->buf[8] = minet_crc8(ann->buf, 8);

		if (minet_hal_write(nif, ann->buf, MINET_FLEN_MIN) == MINET_FLEN_MIN)
		{
			minet_msg_free(ann);
			return 0;
		}
		minet_msg_free(ann);
	}
	return -1;
}
#endif

#if (MINET_NODE)
int minet_rssi(minet_if_t *nif)
{
	if (nif == NULL)
		return 0;

	return nif->rssi;
}
#endif

int minet_clear(minet_if_t *nif)
{
	if (nif == NULL)
		return -1;

	for (int i = 0; i < MINET_RXFIFO_SZ; ++i)
	{
		if (nif->rxfifo[i] != NULL)
		{
			minet_msg_free(nif->rxfifo[i]);
			nif->rxfifo[i] = NULL;
		}
	}
	return 0;
}

int minet_send(minet_if_t *nif, minet_msg_t *msg)
{
	uint8_t flen;
	uint8_t relay;
	int len;
	minet_msg_t *fifo;

	if (!nif || !msg)
		return -1;

	if (msg->addr == nif->addr)
		return -1;

	if (minet_hal_cd(nif) != 0)
		return -1;

	flen = MINET_FLEN_MIN + msg->len;
	fifo = minet_msg_malloc(flen);
	if (fifo == NULL)
		return -1;

	relay = 1 << 6;
#if (MINET_GATEWAY)
	if (nif->addr == MINET_ADDR_GW)
	{
		if (minet_node_relay(nif, msg->addr) == 0)
			relay = 0;
	}
#endif
#if (MINET_NODE)
	if (nif->addr != MINET_ADDR_GW)
	{
		if (msg->addr == MINET_ADDR_GW && nif->direct == 1)
			relay = 0;
	}
#endif

	fifo->buf[0] = flen;
	fifo->buf[1] = ++nif->seq;
	fifo->buf[2] = relay | (MINET_FTYP_DATA << 3);
	fifo->buf[3] = (uint8_t) (msg->addr >> 8);
	fifo->buf[4] = (uint8_t) (msg->addr >> 0);
	fifo->buf[5] = (uint8_t) (nif->addr >> 8);
	fifo->buf[6] = (uint8_t) (nif->addr >> 0);
	len = 7;
	if (msg->len > 0)
	{
		memcpy(&fifo->buf[7], msg->buf, msg->len);
		len += msg->len;
	}
	fifo->buf[len] = minet_sign(nif->nid, fifo->buf, len);
	fifo->buf[len + 1] = minet_crc8(fifo->buf, len + 1);
	fifo->len = (uint16_t) (len + 2);
	if (minet_hal_write(nif, fifo->buf, fifo->len) == fifo->len)
	{
		minet_msg_free(fifo);
		return 0;
	}
	minet_msg_free(fifo);
	return -1;
}

int minet_ack_wait(minet_if_t *nif, int tout)
{
	uint8_t seq;
	uint8_t ftyp;
	int len;
	uint32_t timeout;
	minet_msg_t *fifo;
	minet_msg_t *msg;

	if (!nif || tout < 0)
		return -1;

	fifo = minet_msg_malloc(MINET_FLEN_MAX);
	if (fifo == NULL)
		return -1;

	for (timeout = minet_hal_tick1s() + tout;;)
	{
		len = minet_hal_read(nif, fifo->buf, fifo->len);
		if (minet_msg_legal(nif, fifo->buf, len) == 0)
		{
			seq = minet_parse_seq(fifo->buf);
			ftyp = minet_parse_ctrl_ftyp(fifo->buf);
			if (ftyp == MINET_FTYP_ACK)
			{
				if (seq == nif->seq)
				{
					minet_msg_free(fifo);
					return 0;
				}
			}
			else
			{
				msg = minet_parse(nif, fifo->buf, len);
				if (minet_msgfifo_push(nif->rxfifo, MINET_RXFIFO_SZ, msg) != 0)
					minet_msg_free(msg);
			}
		}

		minet_hal_wdt_feed();
		if (minet_hal_tick1s() > timeout)
			break;
	}
	minet_msg_free(fifo);
	return -1;
}

minet_msg_t *minet_recv(minet_if_t *nif)
{
	int len;
	minet_msg_t *fifo;
	minet_msg_t *msg;

	if (nif == NULL)
		return NULL;

	msg = minet_msgfifo_pop(nif->rxfifo, MINET_RXFIFO_SZ);
	if (msg != NULL)
		return msg;

	fifo = minet_msg_malloc(MINET_FLEN_MAX);
	if (fifo == NULL)
		return NULL;

	len = minet_hal_read(nif, fifo->buf, fifo->len);
	msg = minet_parse(nif, fifo->buf, len);
	minet_msg_free(fifo);
	return msg;
}

void minet_loop_task(minet_if_t *nif)
{
	int len;
	int max;
	minet_msg_t *fifo;
	minet_msg_t *msg;

	if (nif == NULL)
		return;

#if (MINET_GATEWAY)
	if (nif->addr == MINET_ADDR_GW)
	{
		minet_manage(nif);
	}
#endif
#if (MINET_NODE)
	if (nif->addr != MINET_ADDR_GW)
	{
		if (nif->regchl == 0 && nif->tnet > minet_hal_tick1s())/* 暂停注册网络 */
			return;
	}
#endif

	fifo = minet_msg_malloc(MINET_FLEN_MAX);
	if (fifo == NULL)
		return;

	len = minet_hal_read(nif, fifo->buf, fifo->len);
	if (len >= MINET_FLEN_MIN)
	{
		msg = minet_parse(nif, fifo->buf, len);
		if (minet_msgfifo_push(nif->rxfifo, MINET_RXFIFO_SZ, msg) != 0)
			minet_msg_free(msg);
	}
	minet_msg_free(fifo);

	if (minet_hal_cd(nif) == 0)
	{
		msg = NULL;
		max = 0;

#if (MINET_GATEWAY)
		if (nif->addr == MINET_ADDR_GW)
		{
			if (nif->tnet < minet_hal_tick1s())/* announce */
			{
				minet_announce(nif);
				nif->tnet = minet_hal_tick1s() + nif->tpnet;
				return;
			}
		}
#endif
#if (MINET_NODE)
		if (nif->addr != MINET_ADDR_GW)
		{
			if (nif->regchl == 0)/* 需要注册网络 */
			{
				if (nif->tnet < minet_hal_tick1s())
				{
					if (minet_ping(nif, MINET_ADDR_GW, 3) != 0)
					{
						nif->chl += 1;/* TOOD: */
						if (nif->chl == 0)
						{
							nif->tpnet <<= 1;
							nif->tpnet = (nif->tpnet < 5) ? 5 : nif->tpnet;
							nif->tnet = minet_hal_tick1s() + nif->tpnet * 60;/* 暂停注册 */
						}
						else
							minet_hal_chl(nif, nif->chl);/* 尝试注册下一信道 */
					}
					else
					{
						nif->regchl = nif->chl;
						nif->tpnet = 0;
						nif->tnet = minet_hal_tick1s() + MINET_T1S_PING;
						nif->rping = 0;
					}
				}
				return;
			}
			else if (nif->tnet < minet_hal_tick1s())/* ping */
			{
				if (nif->rping)
				{
					if (minet_ping(nif, MINET_ADDR_GW, 3) == 0)
					{
						nif->rping = 0;
						nif->tnet = minet_hal_tick1s() + MINET_T1S_PING;
					}
					else
					{
						if (--nif->rping == 0)
							nif->tnet = minet_hal_tick1s() + MINET_T1S_PING;
						else
							nif->tnet = minet_hal_tick1s() + 2;/* 延时2秒重试 */
					}
				}
				else if (minet_ping(nif, MINET_ADDR_GW, 3) == 0)
				{
					nif->rping = 0;
					nif->tnet = minet_hal_tick1s() + MINET_T1S_PING;
				}
				else
				{
					nif->rping = 2;/* 重ping */
					nif->tnet = minet_hal_tick1s() + 2;/* 延时2秒重试 */
				}
			}
		}
#endif

#if (MINET_RELAY_SZ>0)/* 转发 */
		if (nif->relay && nif->lflag == max++)
		{
			msg = minet_msgfifo_pop(nif->rlfifo, MINET_RELAY_SZ);
			if (msg != NULL)
				sys_delay(minet_hal_random() % 10);
			else
				nif->lflag += 1;
		}
#endif
		if (nif->lflag == max++)/* 响应 */
		{
			msg = minet_msgfifo_pop(nif->ackfifo, MINET_ACK_SZ);
			if (msg != NULL)
				sys_delay(minet_hal_random() % 10);
			else
				nif->lflag += 1;
		}

		if (msg != NULL)
		{
			minet_hal_write(nif, msg->buf, msg->len);
			minet_msg_free(msg);
		}
		nif->lflag = (nif->lflag) % max;
	}
}

/*----------------------------------------------------------------------------*/
int minet_msg_legal(minet_if_t *nif, uint8_t *buf, int len)
{
	uint8_t flen;
	uint8_t sign;
	uint8_t crc;

	if (!nif || !buf || len <= 0)
		return -1;

	if (len < MINET_FLEN_MIN)
	{
		if (nif->err.frame < 255)
			++nif->err.frame;
		return -1;
	}

	flen = buf[0];
	if (flen > len)
	{
		if (nif->err.frame < 255)
			++nif->err.frame;
		return -1;
	}

	crc = buf[flen - 1];
	sign = buf[flen - 2];
	if (minet_crc8(buf, flen - 1) != crc)
	{
		if (nif->err.fcrc < 255)
			++nif->err.fcrc;
		return -1;
	}
	if (minet_sign(nif->nid, buf, flen - 2) != sign)
	{
		if (nif->err.fsign < 255)
			++nif->err.fsign;
		return -1;
	}
	return 0;
}

minet_msg_t *minet_parse(minet_if_t *nif, uint8_t *buf, int len)
{
	uint8_t flen;
	uint8_t seq;
	uint8_t rlhops;
	uint8_t ftyp;
	uint8_t relay;
	uint16_t dest;
	uint16_t src;
	uint8_t *paddr;
	uint8_t *payload;
	minet_msg_t *msg;

//	if (!nif || !buf || len < MINET_FLEN_MIN)
//		return NULL;

	if (minet_msg_legal(nif, buf, len) == 0)
	{
		flen = minet_parse_flen(buf);
		seq = minet_parse_seq(buf);
		rlhops = minet_parse_ctrl_rlhops(buf);
		ftyp = minet_parse_ctrl_ftyp(buf);
		relay = minet_parse_ctrl_relay(buf);
		paddr = minet_parse_paddr(buf);
		payload = minet_parse_payload(buf, rlhops);
		dest = ((uint16_t) paddr[0] << 8) | paddr[1];
		src = ((uint16_t) paddr[2 + 2 * rlhops] << 8) | paddr[3 + 2 * rlhops];

#if (MINET_GATEWAY)
		if (nif->addr == MINET_ADDR_GW)
		{
			if (src == MINET_ADDR_GW && rlhops == 0)/* 附近存在同一信道的网关冲突 */
			{
				if (nif->err.conflict < 255)
					nif->err.conflict += 1;
			}

			if (dest == MINET_ADDR_GW)
			{
				uint8_t *pingdat = ftyp == MINET_FTYP_PING ? payload : NULL;
				minet_node_t *node = minet_node_alloc(dest, rlhops, pingdat);
				minet_node_insert(nif, node);
			}
			relay = 0;/* 网关强制不中继标志 */
		}
#endif
#if (MINET_NODE)
		if (nif->addr != MINET_ADDR_GW && src == MINET_ADDR_GW)
		{
			nif->direct = (rlhops == 0) ? 1 : 0;/* 与网关直连 */
			nif->rssi = minet_hal_rssi(nif);/* 与网关连接rssi */
		}
#endif
		if (dest == nif->addr)/* 本机帧 */
		{
			if (ftyp == MINET_FTYP_DATA)/* 数据帧 */
			{
				flen = flen - MINET_FLEN_MIN - 2 * rlhops;
				msg = minet_msg_malloc(flen);
				if (msg != NULL)
				{
					msg->addr = src;
					msg->len = flen;
					if (flen != 0)
						memcpy(msg->buf, payload, flen);
					minet_ack(nif, seq, src);
					return msg;
				}
			}
			else if (ftyp == MINET_FTYP_PING)/* PING本机 */
			{
				minet_ack(nif, seq, src);
			}
			/* 其它帧不处理了 */
			return NULL;
		}
#if (MINET_RELAY_SZ>0)/* 中继处理 */
		if (relay != 0)
		{
			relay = nif->relay;/* 启用中继标记 */

			if (rlhops >= MINET_RELAY_HOPS)/* 中继次数上限 */
				relay = 0;

			for (int i = 1; relay && i <= rlhops; ++i)
			{
				dest = ((uint16_t) paddr[2 * i] << 8) | paddr[2 * i + 1];
				if (dest == nif->addr)/* 本机在中继地址列表 */
				{
					relay = 0;
					break;
				}
			}
		}
		for (int i = 0; relay && i < MINET_RELAY_SZ; ++i)
		{
			if (nif->rlfifo[i] == NULL)
			{
				flen += 2;
				msg = minet_msg_malloc(flen);
				if (msg != NULL)
				{
					len = (int) (payload - buf);
					memcpy(msg->buf, buf, len);
					rlhops += 1;
					msg->buf[0] = flen;/* flen */
					msg->buf[2] = (msg->buf[2] & 0xF8) + rlhops;/* ctrl */
					len = 7 + 2 * (rlhops - 1) - 2;
					msg->buf[len++] = (uint8_t) (nif->addr >> 8);/* host */
					msg->buf[len++] = (uint8_t) (nif->addr);
					msg->buf[len++] = (uint8_t) (src >> 8);/* src */
					msg->buf[len++] = (uint8_t) (src);
					len = minet_parse_dlen(flen - 2, rlhops - 1);
					if (len > 0)
						memcpy(&msg->buf[7 + 2 * rlhops], payload, len);/* payload */
					msg->buf[flen - 2] = minet_sign(nif->nid, msg->buf,
							flen - 2);
					msg->buf[flen - 1] = minet_crc8(msg->buf, flen - 1);
					nif->rlfifo[i] = msg;/* 添加至中继列表 */
				}
				break;
			}
		}
#endif
	}
	return NULL;
}

int minet_ack(minet_if_t *nif, uint8_t seq, uint16_t dest)
{
	uint8_t relay;
	minet_msg_t *ack;

	if (nif == NULL)
		return -1;

	ack = minet_msg_malloc(MINET_FLEN_MIN);
	if (ack == NULL)
		return -1;

	for (int i = 0; i < MINET_ACK_SZ; ++i)
	{
		if (nif->ackfifo[i] == NULL)
		{
#if (MINET_GATEWAY)
			if (nif->addr == MINET_ADDR_GW)
			{
				relay = minet_node_relay(nif, dest) ? (1 << 6) : 0;
			}
#endif
#if (MINET_NODE)
			if (nif->addr != MINET_ADDR_GW)
			{
				if (dest == MINET_ADDR_GW && nif->direct == 1)
					relay = 0;
				else
					relay = 1 << 6;
			}
#endif

			ack->buf[0] = MINET_FLEN_MIN;
			ack->buf[1] = seq;
			ack->buf[2] = relay | (MINET_FTYP_ACK << 3);
			ack->buf[3] = (uint8_t) (dest >> 8);
			ack->buf[4] = (uint8_t) (dest >> 0);
			ack->buf[5] = (uint8_t) (nif->addr >> 8);
			ack->buf[6] = (uint8_t) (nif->addr >> 0);
			ack->buf[7] = minet_sign(nif->nid, ack->buf, 7);
			ack->buf[8] = minet_crc8(ack->buf, 8);
			nif->ackfifo[i] = ack;
			return 0;
		}
	}

	minet_msg_free(ack);
	return -1;
}

int minet_msgfifo_push(minet_msg_t **fifo, int sz, minet_msg_t *msg)
{
	if (!fifo || sz < 0 || !msg)
		return -1;

	for (int i = 0; i < sz; ++i)
	{
		if (fifo[i] == NULL)
		{
			fifo[i] = msg;
			return 0;
		}
	}
	return -1;
}

minet_msg_t *minet_msgfifo_pop(minet_msg_t **fifo, int sz)
{
	minet_msg_t *msg;

	if (!fifo || sz < 0)
		return NULL;

	for (int i = 0; i < sz; ++i)
	{
		if (fifo[i] != NULL)
		{
			msg = fifo[i];
			fifo[i] = NULL;
			return msg;
		}
	}
	return NULL;
}

void minet_manage(minet_if_t *nif)
{
	if (nif == NULL)
		return;

#if (MINET_GATEWAY)
	uint32_t err;
	uint32_t err_nextchl;
	minet_node_t *curr;

	err_nextchl = (uint32_t) (nif->nodes * 2);
	if (err_nextchl < MINET_NEXTCHL_EMIN)
		err_nextchl = MINET_NEXTCHL_EMIN;

	for (curr = nif->nlis, err = 0; curr != NULL; curr = curr->next)
		err += curr->pingdat[1] + curr->pingdat[2] + curr->pingdat[3];

	if (err > err_nextchl || nif->err.conflict != 0)/* 网络冲突信道跳频 */
	{
		uint32_t rn = minet_hal_random();
		nif->chl = nif->chl + 1 + (rn % 6);
		minet_hal_chl(nif, nif->chl);

		for (curr = nif->nlis; curr != NULL; curr = curr->next)
			memset(&curr->pingdat[1], 0, MINET_PINGDATA_LEN - 1);
	}
#endif

#if (MINET_NODE)
	if (nif->err.pnoack >= MINET_PNOACK_REGCHL)/* noack错误重新注册网络 */
	{
		nif->err.frame = 0;
		nif->err.fcrc = 0;
		nif->err.fsign = 0;
		nif->err.pnoack = 0;
		nif->regchl = 0;/* 重新注册网络 */
	}
#endif
}

#if (MINET_GATEWAY)
int minet_node_insert(minet_if_t *nif, minet_node_t *node)
{
	minet_node_t *curr;

	if (!nif || !node)
		return -1;

	if (nif->nodes >= MINET_NODEMAX)
		return -1;

	if (nif->nlis != NULL)
	{
		for (curr = nif->nlis; curr != NULL; curr = curr->next)
		{
			if (curr->dest == node->dest)
			{
				curr->hops = node->hops;
				memcpy(curr->pingdat, node->pingdat, MINET_PINGDATA_LEN);
				curr->stamp = minet_hal_tick1s();
				minet_node_free(node);
				return 0;
			}
		}
		curr->next = node;
		node->next = NULL;
		nif->nodes += 1;
		return 0;
	}
	else
	{
		nif->nlis = node;
		node->stamp = minet_hal_tick1s();
		node->next = NULL;
		nif->nodes += 1;
	}
	return 0;
}

int minet_node_relay(minet_if_t *nif, uint16_t dest)
{
	minet_node_t *curr;

	if (!nif || dest == nif->addr)
		return 0;

	curr = nif->nlis;
	while (curr != NULL)
	{
		if (curr->dest == dest)
			return curr->hops ? 1 : 0;

		curr = curr->next;
	}
	return 1;
}
#endif
